#include "MIBS.h"
#include "common.h"
#include "set_error_insert.h"
#include <iostream>
#include <stdlib.h>

using namespace std ;

MIBS::MIBS( const int BlockSize , 
		           const int Round , 
				   const int SboxSize , 
				   key_t InitialKey ,
				   plaintext_t Plaintext ,
				   ciphertext_t Ciphertext ) : BaseCipher( BlockSize , Round , SboxSize , InitialKey , Plaintext )
{
}

MIBS::~MIBS(void)
{
}

void MIBS::Encryption(  )
{
	int blocksize = 0 ; 
	int round = 0 ;
	int sboxsize = 0 ;
	key_t initial_key ;
	plaintext_t plaintext ;
	get_CipherInfo( blocksize , round , sboxsize , initial_key , plaintext ) ;
/*	nibble use_key[MIBS_ROUND][MIBS_KEY_NIBBLE] = { 0 } ;*/
	KeySchedule( initial_key ) ;
	bit_to_nibble( plaintext , innerstate , MIBS_SBOX_NUM ) ;
	for ( int i = 0 ; i < round ; ++i )
	{
		error_insert( innerstate , MIBS_INNER_NUM , i ) ;
		OneRound( i ) ;
	}
	nibble temp[MIBS_KEY_NIBBLE] = { 0 } ;
	for ( int i = 0 ; i < MIBS_KEY_NIBBLE ; ++i )
	{
		temp[i] = innerstate[i] ;
		innerstate[i] = innerstate[i+MIBS_KEY_NIBBLE] ;
		innerstate[i+MIBS_KEY_NIBBLE] = temp[i] ; 
	}
	nibble_to_bit( innerstate , MIBS_SBOX_NUM , Ciphertext ) ;
}

void MIBS::Decryption()
{

}

void MIBS::mibs_keyxor( nibble in_key[] , nibble roundkey[] , const int array_num , nibble out_key[] )
{
	int i = 0 ; 
	for ( i = 0 ; i < array_num ; ++i )
	{
		out_key[i] = in_key[i] ^ roundkey[i] ;
	}
}
void MIBS::mibs_sbox( nibble in_sbox[] , const int array_num , nibble out_sbox[] )
{
	for ( int i = 0 ; i < array_num ;  ++i )
	{
		out_sbox[i] = MIBS_SBOX[in_sbox[i]] ;
	}
}

void MIBS::mibs_p( nibble in_p[] , const int array_num , nibble out_p[] )
{
	int i = 0 ;
	for ( i = 0 ; i < array_num ; ++i )
	{
		out_p[MIBS_P[i]] = in_p[i] ;
	}
}

void MIBS::mibs_mix( nibble in_mix[] , const int array_num , nibble out_mix[] )
{
	out_mix[0] = in_mix[1] ^ in_mix[2] ^ in_mix[3] ^ in_mix[4] ^ in_mix[5] ^ in_mix[6] ;
	out_mix[1] = in_mix[0] ^ in_mix[2] ^ in_mix[3] ^ in_mix[5] ^ in_mix[6] ^ in_mix[7] ;
	out_mix[2] = in_mix[0] ^ in_mix[1] ^ in_mix[3] ^ in_mix[4] ^ in_mix[6] ^ in_mix[7] ;
	out_mix[3] = in_mix[0] ^ in_mix[1] ^ in_mix[2] ^ in_mix[4] ^ in_mix[5] ^ in_mix[7] ;
	out_mix[4] = in_mix[0] ^ in_mix[1] ^ in_mix[3] ^ in_mix[4] ^ in_mix[5] ;
	out_mix[5] = in_mix[0] ^ in_mix[1] ^ in_mix[2] ^ in_mix[5] ^ in_mix[6] ;
	out_mix[6] = in_mix[1] ^ in_mix[2] ^ in_mix[3] ^ in_mix[6] ^ in_mix[7] ;
	out_mix[7] = in_mix[0] ^ in_mix[2] ^ in_mix[3] ^ in_mix[4] ^ in_mix[7] ;
}

void MIBS::KeySchedule( key_t InitialKey )
{
	FILE* keyfile = fopen( "f:\\mibs-adfa\\key\\roundkey.txt" , "w+" ) ;
	word userkey[MIBS_WORD_NUM] = { 0 } ;
	bit_to_word( InitialKey , userkey , MIBS_WORD_NUM ) ;
	word keystate[MIBS_WORD_NUM] = { 0 } ;
	for ( int i = 0 ; i < MIBS_WORD_NUM ; ++i )
	{
		keystate[i] = userkey[i] ;
	}
	for ( int i = 0 ; i < MIBS_ROUND ; ++i )
	{
		userkey[1] = keystate[1] ;
		keystate[1] = (keystate[1]>>15) + (keystate[0]<<17) ;
		keystate[0] = (keystate[0]>>15) + (userkey[1]<<17) ;
		keystate[1] = (MIBS_SBOX[(keystate[1]>>28)&0x0f]<<28) + (keystate[1]&0xfffffff) ;
		keystate[0] = keystate[0] ^ ((i+1)*0x800) ;
		for ( int j = 0 ; j < MIBS_KEY_NIBBLE ; ++j )
		{
			RoundKey[i][7-j] = (keystate[1]>>(4*j))&0x0f ;
			for ( int k = 0 ; k < 4 ; ++k )
			{
				bit temp = 0 ; 
				temp = (RoundKey[i][7-j] >> k) & 0x01 ;
				fprintf( keyfile , "k[%d]+%x=0\n" , (32*(i+1)-4*j-k) , temp ) ;
			}
		}
 		printf( "%x \n" , keystate[1] ) ;
	}
}

void MIBS::OneRound( const int round )
{
	nibble leftpart[MIBS_KEY_NIBBLE] = { 0 } ;
	nibble rightpart[MIBS_KEY_NIBBLE] = { 0 } ;
	nibble temp[MIBS_KEY_NIBBLE] = { 0 } ;
	int i = 0 ;
	for ( i = 0 ; i < MIBS_KEY_NIBBLE ; ++i )
	{
		leftpart[i] = innerstate[i] ;
		rightpart[i] = innerstate[i+8] ;
	}
	mibs_keyxor( leftpart , RoundKey[round] , MIBS_KEY_NIBBLE , leftpart ) ;
	mibs_sbox( leftpart , MIBS_KEY_NIBBLE , leftpart ) ;
  	printf( "\nSbox %d  " , round ) ;
  	for ( i = 0 ; i < MIBS_KEY_NIBBLE ; ++i )
  	{
  		printf( "%x" , leftpart[i] ) ;
  	}

	mibs_mix( leftpart , MIBS_KEY_NIBBLE , temp ) ;
	mibs_p( temp , MIBS_KEY_NIBBLE , leftpart ) ;
	for ( i = 0 ; i < MIBS_KEY_NIBBLE ; ++i )
	{
		leftpart[i] = innerstate[i+8] ^ leftpart[i] ;
		innerstate[i+8] = innerstate[i] ;
		innerstate[i] = leftpart[i] ;
	}
}

void MIBS::InvOneRound() 
{

}

void MIBS::print(  )
{
	int b = 0 ; 
	int c = 0 ;
	int a = 0 ;
	key_t d ;
	plaintext_t e ;
	get_CipherInfo( b , c , a , d , e ) ;
	key_t::size_type key_iter = 0 ;
	unsigned int key_size = d.size() ;

  	for ( key_iter = 0 ; key_iter < key_size ; ++key_iter )
  	{
		printf( "%d\n" , d[key_iter] ) ;
  	}
	cout << b << c << endl ;
}




